/** Top-level file for Howl parsing and code generation.
 *  
 * @author Sarah Mount <s.mount@wlv.ac.uk>
 * @version 16 Dec 2009
 * 
 * @see HowlToken
 * @see compiler.test.HowlTestSuite
 */
package compiler;

import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;

import javax.swing.JOptionPane;

import compiler.exceptions.HowlException;
import compiler.exceptions.HowlParserException;
import compiler.nonterminals.AlarmStmt;
import compiler.nonterminals.CreateStmt;
import compiler.nonterminals.DeleteStmt;
import compiler.nonterminals.DeregisterStmt;
import compiler.nonterminals.InsertStmt;
import compiler.nonterminals.RegisterStmt;
import compiler.nonterminals.ReportStmt;

public class HowlCompiler {
	private static final Logger log = Logger.getLogger(HowlCompiler.class.getName());

	/**
	 * For stand-alone testing of the Howl compiler. Uses a JOptionPane to
	 * communicate with the user.
	 * 
	 * @param args
	 *            command-line arguments (unused).
	 * @throws HowlException
	 * @see javax.swing.JOptionPane
	 */
/*	public static void main(String[] args) throws HowlException {
		String input = JOptionPane.showInputDialog(null, "Enter Howl program:",
				"");
		HowlCompiler compiler = new HowlCompiler();
		List<String> queries = new LinkedList<String>();
		try {
			queries = compiler.compile(input);
		} catch (HowlException e) {
			JOptionPane.showMessageDialog(null, e.getMessage(), "Howl error:",
					JOptionPane.ERROR_MESSAGE);
			return;
		}
		String response = "";
		for (String gql : queries) {
			response += gql + "\n";
		}
		JOptionPane.showMessageDialog(null, response, "Generated GQL queries:",
				JOptionPane.PLAIN_MESSAGE);
	}*/

	/** Token reader to lex the input stream. */
	private TokenReader tokenReader;

	/**
	 * Print out debug information.
	 * 
	 */
	private final boolean DEBUG = true;

	/**
	 * Compiler a string into a list of GQL queries to run on AppEngine.
	 * 
	 * @param rawcode
	 *            Howl program to be compiled.
	 * @return list of GQL queries.
	 * @throws HowlException
	 */
	public List<String> compile(String rawcode) throws HowlException {
		this.debugOriginalProgram(rawcode);

		List<HowlToken> parsedPrograms = this.parse(rawcode);

		// Generate and return a list of GQL queries.
		List<String> queries = new LinkedList<String>();
		for (HowlToken program : parsedPrograms) {
			String query = program.codeGenerate();
			queries.add(query);
		}
		this.debugGQLQueries(queries);
		return queries;
	}

	/**
	 * Print GQL queries to STDOUT for debugging.
	 * 
	 * @param queries
	 *            list of queries.
	 */
	private void debugGQLQueries(List<String> queries) {
		
		if (this.DEBUG) {
			String info = "Generated GQL queries: \n";
			for (String query : queries) {
				info = info +query + "\n";
			}
			log.info(info);
		}
	}

	/**
	 * Print original program to STDOUT for debugging.
	 * 
	 * @param rawcode
	 *            Howl program.
	 */
	private void debugOriginalProgram(String rawcode) {
		
		if (this.DEBUG) {
			log.info("Original program:\n" + rawcode);
			/*System.out.println("Original program:\n" + rawcode);
			System.out.println();
			*/
			}
	}

	/**
	 * Print a list of <code>HowlToken</code> types to STDOUT for debugging.
	 * 
	 * @param parsedPrograms
	 *            list of parsed tokens.
	 * @see compiler.HowlToken
	 */
	private void debugParsedProgram(List<HowlToken> parsedPrograms) {
	
		if (this.DEBUG) {
			
			String info = "Parsed tokens:\n";
			for (HowlToken program : parsedPrograms) {
				info = info + program.toString() +"\n";
			}
			log.info(info);
			//System.out.println();
		}
	}

	/**
	 * Parse a string into a list of HowlTokens. Each element of the list is a
	 * single Howl statement. Statements should be separated by semicolons.
	 * 
	 * @param rawcode
	 *            original Howl program.
	 * @return list of parsed tokens.
	 * @throws HowlParserException
	 * @see compiler.HowlToken
	 */
	public List<HowlToken> parse(String rawcode) throws HowlParserException {
		this.tokenReader = new TokenReader(rawcode);

		List<HowlToken> parsedPrograms = new LinkedList<HowlToken>();

		while (this.tokenReader.hasMoreTokens()) {
			String lookahead = this.tokenReader.nextToken();
			HowlToken howlstmt = null;
			if (lookahead.equals("create")) {
				howlstmt = new CreateStmt(this.tokenReader).parse();
			} else if (lookahead.equals("register")) {
				howlstmt = new RegisterStmt(this.tokenReader).parse();
			} else if (lookahead.equals("deregister")) {
				howlstmt = new DeregisterStmt(this.tokenReader).parse();
			} else if (lookahead.equals("insert")) {
				howlstmt = new InsertStmt(this.tokenReader).parse();
			} else if (lookahead.equals("delete")) {
				howlstmt = new DeleteStmt(this.tokenReader).parse();
			} else if(lookahead.equals("alarm")){
				howlstmt = new AlarmStmt(this.tokenReader).parse();
			} else if(lookahead.equals("report")){
				howlstmt = new ReportStmt(this.tokenReader).parse();
			}
			// Add other grammar productions.
			else
				throw new HowlParserException(this.tokenReader.getStatement(),
						this.tokenReader.getCharNo());
			if (howlstmt != null) {
				parsedPrograms.add(howlstmt);
			}
			this.tokenReader.consumeSemiColon();
		}
		this.debugParsedProgram(parsedPrograms);
		return parsedPrograms;
	}
}
